library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
use work.constants.all;

entity regfile is
port (
 	rst		:	in std_logic;
	clk		:	in std_logic;
	addr 		: 	in std_logic_vector(7 downto 0);
	wrBit		:	in std_logic;
	wrByte	:	in std_logic;
	rdBit		:	in std_logic;
	rdByte	:	in std_logic;
	
	diBit		:	in std_logic;
	diByte	:	in std_logic_vector(7 downto 0);

	doBit		:	out std_logic;
	doByte	:	out std_logic_vector(7 downto 0);

	P0_out	:	out std_logic_vector(7 downto 0);
	P1_out	:	out std_logic_vector(7 downto 0);
	P2_out	:	out std_logic_vector(7 downto 0);
	P3_out	:	out std_logic_vector(7 downto 0);
	
	--======= Interrupt Signal ==========
	erase_int	:	in 	std_logic;
	next_int		:	out 	std_logic_vector(3 downto 0);
	Serving_int		:	in 	std_logic_vector(3 downto 0);

	P0_in		:	in std_logic_vector(7 downto 0);
	P1_in		:	in std_logic_vector(7 downto 0);
	P2_in		:	in std_logic_vector(7 downto 0);
	P3_in		:	in std_logic_vector(7 downto 0)
);
end entity;

architecture regarch of regfile is

component Customized_Interrupt_Handler is
Port ( 
	  clk 						: in  STD_LOGIC;
	  rst 						: in  STD_LOGIC;
	  CLEAR_CURR_INTERRUPT 	: in  STD_LOGIC;
	  FLAG_CHANGED 			: out  STD_LOGIC;
	  ACK_CHANGED 				: in  STD_LOGIC;
	  CURR_INT 					: in  STD_LOGIC_VECTOR (3 downto 0);
	  NEXT_INT 					: out  STD_LOGIC_VECTOR (3 downto 0);
	  IE_reg 					: in  STD_LOGIC_VECTOR (7 downto 0);
	  IP_reg 					: in  STD_LOGIC_VECTOR (7 downto 0);
	  TCON_reg 					: in  STD_LOGIC_VECTOR (7 downto 0);
	  OUT_TCON_reg 			: out  STD_LOGIC_VECTOR (7 downto 0);
	  INT0 						: in  STD_LOGIC;
	  INT1 						: in  STD_LOGIC;
	  RI 							: in  STD_LOGIC;
	  TI 							: in  STD_LOGIC);
end component;

component Timer_Handler is
Port ( 
	  clk : in  STD_LOGIC;
	  rst : in  STD_LOGIC;
	  TMOD_reg : in  STD_LOGIC_VECTOR (7 downto 0);
	  TCON_reg : in  STD_LOGIC_VECTOR (7 downto 0);
	  TH0_reg : in  STD_LOGIC_VECTOR (7 downto 0);
	  TL0_reg : in  STD_LOGIC_VECTOR (7 downto 0);
	  TH1_reg : in  STD_LOGIC_VECTOR (7 downto 0);
	  TL1_reg : in  STD_LOGIC_VECTOR (7 downto 0);
	  T1		 : in STD_LOGIC;
	  T0		 : in STD_LOGIC;
	  INT1	 : in STD_LOGIC;
	  INT0	 : in STD_LOGIC;
	  Timer_Changed : out  STD_LOGIC;
	  ack_changed : in  STD_LOGIC;
	  OUT_TH0_reg : out  STD_LOGIC_VECTOR (7 downto 0);
	  OUT_TL0_reg : out  STD_LOGIC_VECTOR (7 downto 0);
	  OUT_TH1_reg : out  STD_LOGIC_VECTOR (7 downto 0);
	  OUT_TL1_reg : out  STD_LOGIC_VECTOR (7 downto 0);
	  OUT_TCON_reg : out  STD_LOGIC_VECTOR (7 downto 0));
end component;

	signal ACC	:	std_logic_vector(7 downto 0);
	signal B	:	std_logic_vector(7 downto 0);
	signal DPH	:	std_logic_vector(7 downto 0);
	signal DPL	:	std_logic_vector(7 downto 0);
	signal IE	:	std_logic_vector(7 downto 0);
	signal IP	:	std_logic_vector(7 downto 0);
	signal PCON	:	std_logic_vector(7 downto 0);
	signal PSW	:	std_logic_vector(7 downto 0);
	signal SBUF	:	std_logic_vector(7 downto 0);
	signal SCON	:	std_logic_vector(7 downto 0);
	signal SP	:	std_logic_vector(7 downto 0);
	signal TCON	:	std_logic_vector(7 downto 0);
	signal TCON_temp	:	std_logic_vector(7 downto 0);
	signal TH0	:	std_logic_vector(7 downto 0);
	signal TH1	:	std_logic_vector(7 downto 0);
	signal TL0	:	std_logic_vector(7 downto 0);
	signal TL1	:	std_logic_vector(7 downto 0);
	signal TMOD	:	std_logic_vector(7 downto 0);
	signal P0	:	std_logic_vector(7 downto 0);
	signal P1	:	std_logic_vector(7 downto 0);
	signal P2	:	std_logic_vector(7 downto 0);
	signal P3	:	std_logic_vector(7 downto 0);

	--====================== Timer Signal =========================
	signal Timer_Changed : STD_LOGIC;
	signal ack_Timer	 : STD_LOGIC;
	signal Timer_TH0	 : STD_LOGIC_VECTOR (7 downto 0);
	signal Timer_TL0	 : STD_LOGIC_VECTOR (7 downto 0);
	signal Timer_TH1	 : STD_LOGIC_VECTOR (7 downto 0);
	signal Timer_TL1	 : STD_LOGIC_VECTOR (7 downto 0);
	signal Timer_TCON	 : STD_LOGIC_VECTOR (7 downto 0);
	
	--====================== Interrupt Signal ========================
	signal Interrupt_Changed	: STD_LOGIC;
	signal ack_Interrupt	 : STD_LOGIC;
	signal Interrupt_TCON	 : STD_LOGIC_VECTOR (7 downto 0);
	signal TCON_Update_Condition : STD_LOGIC_VECTOR (1 downto 0);
begin

int_handler:	Customized_Interrupt_Handler
port map
(
	clk => clk,
	rst => rst,
	CLEAR_CURR_INTERRUPT => erase_int,
	FLAG_CHANGED => Interrupt_Changed,
	ACK_CHANGED => ack_Interrupt,
	CURR_INT => Serving_int,
	NEXT_INT => next_int,
	IE_reg => IE,
	IP_reg => IP,
	TCON_reg => TCON,
	OUT_TCON_reg => Interrupt_TCON,
	INT0 => P3_in(2),
	INT1 => P3_in(3),
	RI => SCON(0),
	TI => SCON(1)
);

Timer_Control:	Timer_Handler
port map
(
	clk => clk,
	rst => rst,
	TMOD_reg => TMOD,
	TCON_reg => TCON,
	TH0_reg	=> TH0,
	TL0_reg	=> TL0,
	TH1_reg	=> TH1,
	TL1_reg	=> TL1,
	T1			=> P3_in(5),
	T0			=> P3_in(4),
	INT1		=> P3_in(3),
	INT0		=> P3_in(2),
	Timer_Changed	=> Timer_Changed,
	ack_changed		=> ack_Timer,
	OUT_TH0_reg		=> Timer_TH0,
	OUT_TL0_reg		=> Timer_TL0,
	OUT_TH1_reg		=> Timer_TH1,
	OUT_TL1_reg		=> Timer_TL1,
	OUT_TCON_reg	=> Timer_TCON
);

TCON_Update_Condition <= Timer_Changed & Interrupt_Changed;

	process (clk, rst, rdByte, rdBit, addr, Timer_Changed, Interrupt_Changed)
		variable U	:	std_logic_vector(7 downto 0);
		variable L	:	INTEGER;
begin
		
	--TCON <= TCON_temp; 
	if (rst = '1') then
		ACC    <= "00000000";
      B      <= "00000000";
      DPH    <= "00000000";
      DPL    <= "00000000";
      IE     <= "00000000";
      IP     <= "00000000";
      PCON   <= "00000000";
      PSW    <= "00000000";
      SBUF   <= "00000000";
      SCON   <= "00000000";
      SP     <= "00000111";
      TCON   <= "00000000";
      TH0    <= "00000000";
      TH1    <= "00000000";
      TL0    <= "00000000";
      TL1    <= "00000000";
      TMOD   <= "00000000";
      P0_out <= "00000000";
      P1_out <= "00000000";
      P2_out <= "00000000";
      P3_out <= "00000000";
		P0	 <= "00000000";
		P1	 <= "00000000";
		P2	 <= "00000000";
		P3	 <= "00000000";
		doByte <= "ZZZZZZZZ";
		doBit <= 'Z';
		
		ack_Timer <= '0';
		ack_Interrupt <= '0';
	else	
		if (rdByte = '1') then
			case addr is
					when xE0   => doByte <= ACC; 
					when xF0   => doByte <= B;	   
					when x83   => doByte <= DPH; 
					when x82   => doByte <= DPL;	
					when xA8   => doByte <= IE;	  
					when xB8   => doByte <= IP;	  
					when x80   => doByte <= P0;	  
					when x90   => doByte <= P1;	  
					when xA0   => doByte <= P2;	  
					when xB0   => doByte <= P3_in;	  
					when x87   => doByte <= PCON;	
					when xD0   => doByte <= PSW;	 
					when x99   => doByte <= SBUF;	 
					when x98   => doByte <= SCON;	 
					when x81   => doByte <= SP;	  
					when x88   => doByte <= TCON;
					when x8C   => doByte <= TH0;	 
					when x8D   => doByte <= TH1;	  
					when x8A   => doByte <= TL0;	  
					when x8B   => doByte <= TL1;	  
					when x89   => doByte <= TMOD;	  
					when others =>	doByte <= "ZZZZZZZZ";
				end case;
				
			-- ====================== Timer & Interrupt update Session =============================
			case TCON_Update_Condition is
			when "01"|"11"	=>
				ack_Timer 		<= '1';
				ack_Interrupt	<=	'1';
				TCON			<= Interrupt_TCON;
			when "10"		=>
				ack_Timer 		<= '1';
				ack_Interrupt	<=	'0';
				TH0 			<= Timer_TH0;
				TL0			<= Timer_TL0;
				TH1			<=	Timer_TH1;
				TL1			<= Timer_TL1;
				TCON			<= Timer_TCON;
			when others		=>
				ack_Timer 		<= '0';
				ack_Interrupt	<=	'0';
				end case;

--			if( Timer_Changed = '1') then
--				ack_Timer 	<= '1';
--				TH0 			<= Timer_TH0;
--				TL0			<= Timer_TL0;
--				TH1			<=	Timer_TH1;
--				TL1			<= Timer_TL1;
--				TCON			<= Timer_TCON;
--			else
--				ack_Timer 	<= '0';
--			end if;
--			
--			if( Interrupt_Changed = '1' ) then
--				-- Missing Interrupt update might lead to disaster, update interrupt higher Priority
--				ack_Interrupt <= '1';
--				TCON <= Interrupt_TCON;
--			else
--				ack_Interrupt <= '0';
--			end if;
			
--			if( Interrupt_Changed = '1' ) then
--				-- Missing Interrupt update might lead to disaster, update interrupt higher Priority
--				ack_Interrupt <= '1';
--				ack_Timer 	<= '0';
--				TCON <= Interrupt_TCON;
--			elsif( Timer_Changed ='1') then
--				ack_Timer 	<= '1';
--				ack_Interrupt <= '0';
--				TH0 			<= Timer_TH0;
--				TL0			<= Timer_TL0;
--				TH1			<=	Timer_TH1;
--				TL1			<= Timer_TL1;
--				TCON			<= Timer_TCON;
--			else				
--				ack_Timer 	<= '0';
--				ack_Interrupt <= '0';
--			end if;

		elsif (rdBit = '1') then
			L := conv_integer(addr(2 downto 0));
			U := addr(7 downto 3)&"000";
				case U is
					when xE0   => doBit <= ACC(L);
					when xF0   => doBit <= B(L);
					when xA8   => doBit <= IE(L);
					when xB8   => doBit <= IP(L);
					when x80   => doBit <= P0(L);
					when x90   => doBit <= P1(L);
					when xA0   => doBit <= P2(L);
					when xB0   => doBit <= P3_in(L);
					when xD0   => doBit <= PSW(L);
					when x81   => doBit <= SP(L);
					when x98   => doBit <= SCON(L);
					when x88   => doBit <= TCON(L);
					when others =>	doBit <= 'Z';	
				end case;
				
			-- ====================== Timer & Interrupt update Session =============================
			case TCON_Update_Condition is
			when "01"|"11"	=>
				ack_Timer 		<= '0';
				ack_Interrupt	<=	'1';
				TCON			<= Interrupt_TCON;
			when "10"		=>
				ack_Timer 		<= '1';
				ack_Interrupt	<=	'0';
				TH0 			<= Timer_TH0;
				TL0			<= Timer_TL0;
				TH1			<=	Timer_TH1;
				TL1			<= Timer_TL1;
				TCON			<= Timer_TCON;
			when others		=>
				ack_Timer 		<= '0';
				ack_Interrupt	<=	'0';
			end case;
			
--			if( Timer_Changed = '1') then
--				ack_Timer 	<= '1';
--				TH0 			<= Timer_TH0;
--				TL0			<= Timer_TL0;
--				TH1			<=	Timer_TH1;
--				TL1			<= Timer_TL1;
--				TCON			<= Timer_TCON;
--			else
--				ack_Timer 	<= '0';
--			end if;
--			
--			if( Interrupt_Changed = '1' ) then
--				-- Missing Interrupt update might lead to disaster, update interrupt higher Priority
--				ack_Interrupt <= '1';
--				TCON <= Interrupt_TCON;
--			else
--				ack_Interrupt <= '0';
--			end if;
			
--			if( Interrupt_Changed = '1' ) then
--				-- Missing Interrupt update might lead to disaster, update interrupt higher Priority
--				ack_Interrupt <= '1';
--				ack_Timer 	<= '0';
--				TCON <= Interrupt_TCON;
--			elsif( Timer_Changed ='1') then
--				ack_Timer 	<= '1';
--				ack_Interrupt <= '0';
--				TH0 			<= Timer_TH0;
--				TL0			<= Timer_TL0;
--				TH1			<=	Timer_TH1;
--				TL1			<= Timer_TL1;
--				TCON			<= Timer_TCON;
--			else				
--				ack_Timer 	<= '0';
--				ack_Interrupt <= '0';
--			end if;

		elsif (clk' event and clk = '1') then
			
			if (wrByte = '1') then
					case addr is
						when xE0   => ACC <= diByte; 
						when xF0   => B <= diByte;	   
						when x83   => DPH <= diByte; 
						when x82   => DPL <= diByte;	
						when xA8   => IE <= diByte;	  
						when xB8   => IP <= diByte;	  
						when x80   => P0_out <= diByte;	  
								  P0	 <= diByte;	  
						when x90   => P1_out <= diByte;	 
								  P1	 <= diByte;	   
						when xA0   => P2_out <= diByte;	  
								  P2	 <= diByte;	  
						when xB0   => P3_out <= diByte;
								  P3	 <= diByte;	  
						when x87   => PCON <= diByte;	
						when xD0   => PSW <= diByte;	 
						when x99   => SBUF <= diByte;	 
						when x98   => SCON <= diByte;	 
						when x81   => SP <= diByte;	  
						when x88   => TCON <= diByte;
						when x8C   => TH0 <= diByte;	 
						when x8D   => TH1 <= diByte;	  
						when x8A   => TL0 <= diByte;	  
						when x8B   => Tl1 <= diByte;	  
						when x89   => TMOD <= diByte;	  
					when others =>			
					end case;		
			
			elsif (wrBit = '1') then
				L := conv_integer(addr(2 downto 0));
				U := addr(7 downto 3)&"000";
				case U is
						when xE0   => ACC(L) <= diBit;
						when xF0   => B(L)<= diBit;
						when xA8   => IE(L)<= diBit;
						when xB8   => IP(L)<= diBit;
						when x80   => P0_out(L)<= diBit;
									 P0(L)<= diBit;
						when x90   => P1_out(L)<= diBit;
									 P1(L)<= diBit;
						when xA0   => P2_out(L)<= diBit;
									 P2(L)<= diBit;
						when xB0   => P3_out(L)<= diBit;
									 P3(L)<= diBit;
						when xD0   => PSW(L)<= diBit;
						when x81   => SP(L)<= diBit;
						when x98   => SCON(L)<= diBit;
						when x88   => TCON(L)<= diBit;
					when others =>			
					end case;
			else
				--====================== Timer & Interrupt update Session =============================
				case TCON_Update_Condition is
				when "01"|"11"	=>
					ack_Timer 		<= '0';
					ack_Interrupt	<=	'1';
					TCON			<= Interrupt_TCON;
				when "10"		=>
					ack_Timer 		<= '1';
					ack_Interrupt	<=	'0';
					TH0 			<= Timer_TH0;
					TL0			<= Timer_TL0;
					TH1			<=	Timer_TH1;
					TL1			<= Timer_TL1;
					TCON			<= Timer_TCON;
				when others		=>
					ack_Timer 		<= '0';
					ack_Interrupt	<=	'0';
				end case;
				
	--			if( Timer_Changed = '1') then
	--				ack_Timer 	<= '1';
	--				TH0 			<= Timer_TH0;
	--				TL0			<= Timer_TL0;
	--				TH1			<=	Timer_TH1;
	--				TL1			<= Timer_TL1;
	--				TCON			<= Timer_TCON;
	--			else
	--				ack_Timer 	<= '0';
	--			end if;
	--			
	--			if( Interrupt_Changed = '1' ) then
	--				-- Missing Interrupt update might lead to disaster, update interrupt higher Priority
	--				ack_Interrupt <= '1';
	--				TCON <= Interrupt_TCON;
	--			else
	--				ack_Interrupt <= '0';
	--			end if;

				
	--			if( Interrupt_Changed = '1' ) then
	--				-- Missing Interrupt update might lead to disaster, update interrupt higher Priority
	--				ack_Interrupt <= '1';
	--				ack_Timer 	<= '0';
	--				TCON <= Interrupt_TCON;
	--			elsif( Timer_Changed ='1') then
	--				ack_Timer 	<= '1';
	--				ack_Interrupt <= '0';
	--				TH0 			<= Timer_TH0;
	--				TL0			<= Timer_TL0;
	--				TH1			<=	Timer_TH1;
	--				TL1			<= Timer_TL1;
	--				TCON			<= Timer_TCON;
	--			else				
	--				ack_Timer 	<= '0';
	--				ack_Interrupt <= '0';
	--			end if;
			end if;
		end if;
	end if;		
end process;
end regarch;